﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="MainWindow.xaml.cs" company="GT">
//   Gunther Taborga
// </copyright>
// <summary>
//   Interaction logic for MainWindow.xaml
// </summary>
// --------------------------------------------------------------------------------------------------------------------

// TODO refactored=true
namespace BatchID3.Editor
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Configuration;
    using System.IO;
    using System.Linq;
    using System.Windows;
    using System.Windows.Input;

    using BatchID3.Editor.Dialogs;
    using BatchID3.Editor.Extensions;
    using BatchID3.Editor.Plugins;
    using BatchID3.Enumerations;
    using BatchID3.Extensions;
    using BatchID3.Mp3;

    using BackgroundWorker = System.ComponentModel.BackgroundWorker;
    using KeyEventArgs = System.Windows.Input.KeyEventArgs;
    using MessageBoxOptions = System.Windows.MessageBoxOptions;

    public partial class MainWindow : Window, IWindow
    {
        private const string DefaultFileOpenDialogExtension = "mp3";

        private readonly IFolderBrowserDialogFactory folderBrowserDialogFactory;

        private readonly IOpenFileDialogFactory openFileDialogFactory;

        private readonly IRenameDialogFactory renameDialogFactory;

        private readonly ISortDialogFactory sortDialogFactory;

        private readonly ISetTagDialogFactory setTagDialogFactory;

        private readonly IMp3EntityFactory mp3EntityFactory;

        private readonly IMp3FileSystemManager mp3FileSystemManager;

        private readonly IWinamp winamp;

        private readonly IBindingList mp3EntityList;

        private string previouslyLoadedFilesBrowseDirectory = string.Empty;

        private string previousSortDestinationDirectory = string.Empty;

        public MainWindow()
        {
            this.InitializeComponent();
        }

        public MainWindow(
            IFolderBrowserDialogFactory folderBrowserDialogFactory,
            IOpenFileDialogFactory openFileDialogFactory,
            IRenameDialogFactory renameDialogFactory,
            ISortDialogFactory sortDialogFactory,
            ISetTagDialogFactory setTagDialogFactory,
            IMp3PreviewFactory mp3PreviewFactory,
            IMp3EntityFactory mp3EntityFactory, 
            IMp3FileSystemManager mp3FileSystemManager, 
            IWinamp winamp,
            IBindingList mp3EntityList)
        {
            this.InitializeComponent();

            this.folderBrowserDialogFactory = folderBrowserDialogFactory;
            this.openFileDialogFactory = openFileDialogFactory;
            this.renameDialogFactory = renameDialogFactory;
            this.sortDialogFactory = sortDialogFactory;
            this.setTagDialogFactory = setTagDialogFactory;
            this.mp3EntityFactory = mp3EntityFactory;
            this.mp3FileSystemManager = mp3FileSystemManager;
            this.winamp = winamp;
            this.mp3EntityList = mp3EntityList;
        }

        private SearchOption CurrentSearchOption
        {
            get
            {
                return (this.recurseCheckbox.IsChecked == true)
                           ? SearchOption.AllDirectories
                           : SearchOption.TopDirectoryOnly;
            }
        }

        private string FolderPath
        {
            get
            {
                return this.mp3FileSystemManager.FileSystem.GetActualPath(this.loadedDirectoryTextbox.Text);
            }

            set
            {
                if (!string.IsNullOrEmpty(value))
                {
                    this.loadedDirectoryTextbox.Text = value;
                }
            }
        }

        private IEnumerable<IMp3Entity> SelectedMp3Entities
        {
            get
            {
                var selectedItems = this.mp3DataGrid.SelectedItems as IEnumerable<object>;

                return selectedItems == null
                           ? Enumerable.Empty<IMp3Entity>()
                           : selectedItems.Select(item => item as IMp3Entity);
            }
        }

        #region Set Folder

        private void SetFolderMenuItemClick(object sender, RoutedEventArgs e)
        {
            this.ShowFolderBrowserDialog();
        }

        private void BrowseButtonClick(object sender, RoutedEventArgs e)
        {
            this.ShowFolderBrowserDialog();
        }

        private void ShowFolderBrowserDialog()
        {
            using (var folderBrowserDialog = this.folderBrowserDialogFactory.GetFolderBrowserDialog())
            {
                folderBrowserDialog.Description = Properties.Resources.find_mp3_folder;
                folderBrowserDialog.RootFolder = Environment.SpecialFolder.MyComputer;

                if (!string.IsNullOrEmpty(this.previouslyLoadedFilesBrowseDirectory))
                {
                    folderBrowserDialog.SelectedPath = this.previouslyLoadedFilesBrowseDirectory;
                }

                if (folderBrowserDialog.ShowDialog(this.GetIWin32Window()) != System.Windows.Forms.DialogResult.OK)
                {
                    return;
                }

                if (this.IsValidMp3Path(folderBrowserDialog.SelectedPath))
                {
                    this.FolderPath = this.previouslyLoadedFilesBrowseDirectory = folderBrowserDialog.SelectedPath;
                }
            }
        }

        private void LoadFilesButtonClick(object sender, RoutedEventArgs e)
        {
            this.LoadNewMp3Files();
        }

        private void LoadedDirectoryTextboxKeyDown(object sender, KeyEventArgs e)
        {
            if (this.loadFilesButton.IsEnabled && e.Key == Key.Enter)
            {
                this.LoadNewMp3Files();
            }
        }

        private void LoadNewMp3Files(SearchOption searchOption)
        {
            this.SetLoadFilesButtonState(false);
            this.mp3EntityList.Clear();
            this.loadedDirectoryTextbox.Text = this.mp3FileSystemManager.FileSystem.GetActualPath(this.FolderPath);

            var backgroundWorker = new BackgroundWorker { WorkerReportsProgress = true };
            backgroundWorker.DoWork += this.AddFolderDoWork;
            backgroundWorker.ProgressChanged += this.AddFolderProgressChanged;
            backgroundWorker.RunWorkerCompleted += this.AddFolderRunWorkerCompleted;
            backgroundWorker.RunWorkerAsync(new FolderSearchSpecification(this.FolderPath, searchOption));
        }

        private void LoadNewMp3Files()
        {
            this.LoadNewMp3Files(this.CurrentSearchOption);
        }

        private void AddFolderDoWork(object sender, DoWorkEventArgs e)
        {
            var backgroundWorker = (BackgroundWorker)sender;
            var args = (FolderSearchSpecification)e.Argument;

            var mp3FilePaths = this.mp3FileSystemManager.GetMp3FilePaths(args.Directory, args.SearchOption).ToList();

            for (var i = 0; i < mp3FilePaths.Count; i++)
            {
                var mp3FilePath = mp3FilePaths[i];
                var mp3 = this.mp3EntityFactory.CreateMp3Entity(mp3FilePath);

                backgroundWorker.ReportProgress(i.PercentOf(mp3FilePaths.Count), new[] { mp3 });
            }
        }

        private void AddFolderProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            var mp3Entities = e.UserState as IEnumerable<IMp3Entity>;

            if (mp3Entities != null)
            {
                this.AddMp3Entities(mp3Entities);
            }

            this.SetProgressBar(e.ProgressPercentage);
        }

        private void AddFolderRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            this.SetLoadFilesButtonState(true);
            this.ResetProgressBar();
        }

        #endregion

        #region Set Files

        private void SetFilesMenuItemClick(object sender, RoutedEventArgs e)
        {
            this.ShowOpenFileDialog();
        }

        private void ShowOpenFileDialog()
        {
            using (var openFileDialog = this.openFileDialogFactory.GetOpenFileDialog())
            {
                openFileDialog.Filter = Properties.Resources.mp3_file_dialog_filter;
                openFileDialog.DefaultExt = DefaultFileOpenDialogExtension;
                openFileDialog.Multiselect = true;

                if (openFileDialog.ShowDialog(this.GetIWin32Window()) != System.Windows.Forms.DialogResult.OK)
                {
                    return;
                }

                this.mp3EntityList.Clear();

                var backgroundWorker = new BackgroundWorker { WorkerReportsProgress = true };
                backgroundWorker.DoWork += this.AddFileDoWork;
                backgroundWorker.ProgressChanged += this.AddFileProgressChanged;
                backgroundWorker.RunWorkerCompleted += (sender, args) => this.ResetProgressBar();
                backgroundWorker.RunWorkerAsync(openFileDialog.FileNames);
            }
        }

        private void AddFileDoWork(object sender, DoWorkEventArgs e)
        {
            var backgroundWorker = (BackgroundWorker)sender;
            var mp3FilePaths = ((IEnumerable<string>)e.Argument).ToList();

            for (var i = 0; i < mp3FilePaths.Count; i++)
            {
                var mp3 = this.mp3EntityFactory.CreateMp3Entity(mp3FilePaths[i]);
                backgroundWorker.ReportProgress(i.PercentOf(mp3FilePaths.Count), mp3);
            }

            backgroundWorker.ReportProgress(100, null);
        }

        private void AddFileProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            var mp3 = e.UserState as IMp3Entity;

            if (mp3 != null)
            {
                this.AddMp3Entity(mp3);
            }

            this.SetProgressBar(e.ProgressPercentage);
        }

        #endregion

        #region Rename

        private void RenameMenuItemClick(object sender, RoutedEventArgs e)
        {
            var tagTokens = new[] { TagToken.Album, TagToken.Track, TagToken.Artist, TagToken.AlbumArtist, TagToken.Title, TagToken.BeatsPerMinute };
            var preSelectedTagTokens = new[] { TagToken.Artist, TagToken.Album, TagToken.Track, TagToken.Title };
            var delimiters = new[] { '-', '_' };
            
            var renameDialog = this.renameDialogFactory.GetRenameDialog(this.SelectedMp3Entities, tagTokens, preSelectedTagTokens, delimiters);
            renameDialog.RenameProgressChanged += (o, args) => this.SetProgressBar(args.Progress);
            renameDialog.ShowDialog();
        }

        #endregion

        #region Delete

        private void DeleteMenuItemClick(object sender, RoutedEventArgs e)
        {
            var mp3Entities = this.SelectedMp3Entities;

            if (this.ShowDeleteFileMessageBox(mp3Entities) != MessageBoxResult.Yes)
            {
                return;
            }

            var backgroundWorker = new BackgroundWorker { WorkerReportsProgress = true };
            backgroundWorker.DoWork += this.DeleteFileDoWork;
            backgroundWorker.ProgressChanged += this.DeleteFileProgressChanged;
            backgroundWorker.RunWorkerCompleted += (o, args) => this.ResetProgressBar();
            backgroundWorker.RunWorkerAsync(mp3Entities);
        }

        private void DeleteFileProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            var mp3 = e.UserState as IMp3Entity;
            this.mp3EntityList.Remove(mp3);

            this.SetProgressBar(e.ProgressPercentage);
        }

        private void DeleteFileDoWork(object sender, DoWorkEventArgs e)
        {
            var backgroundWorker = (BackgroundWorker)sender;
            var mp3Entities = ((IEnumerable<IMp3Entity>)e.Argument).ToList();

            for (var i = 0; i < mp3Entities.Count; i++)
            {
                var mp3 = mp3Entities[i];
                this.mp3FileSystemManager.Delete(mp3);

                backgroundWorker.ReportProgress(i.PercentOf(mp3Entities.Count), mp3);
            }

            backgroundWorker.ReportProgress(100, null);
        }

        private MessageBoxResult ShowDeleteFileMessageBox(IEnumerable<IMp3Entity> mp3Entities)
        {
            var messageBoxResult =
                System.Windows.MessageBox.Show(
                    string.Format("Are you sure you want to delete {0} selected mp3 file(s)?", mp3Entities.Count()),
                    "Delete File",
                    MessageBoxButton.YesNo,
                    MessageBoxImage.Question,
                    MessageBoxResult.No,
                    MessageBoxOptions.None);

            return messageBoxResult;
        }

        #endregion

        #region Sort

        private void SortMenuItemClick(object sender, RoutedEventArgs e)
        {
            var tagTokens = new[] { TagToken.Artist, TagToken.AlbumArtist, TagToken.Album, TagToken.BeatsPerMinute };
            var preSelectedTagTokens = new[] { TagToken.AlbumArtist, TagToken.Album };
            var sortDialog = this.sortDialogFactory.GetSortDialog(this.SelectedMp3Entities, tagTokens, preSelectedTagTokens, this.FolderPath, this.previousSortDestinationDirectory);
            sortDialog.SortProgressChanged += this.SortDialogSortProgressChanged;
            sortDialog.ShowDialog();

            this.previousSortDestinationDirectory = sortDialog.DestinationDirectory;
        }

        private void SortDialogSortProgressChanged(object sender, SortProgressChangedEventArgs e)
        {
            if (e.Mp3Entity != null && e.FileMovedOutOfScope)
            {
                this.mp3EntityList.Remove(e.Mp3Entity);
            }

            this.SetProgressBar(e.Progress);
        }

        #endregion

        #region Set Tag

        private void SetTagMenuItemClick(object sender, RoutedEventArgs e)
        {
            this.ShowSetTagDialog();
        }

        private void ShowSetTagDialog()
        {
            var tagTokens = new[] { TagToken.Album, TagToken.Track, TagToken.Artist, TagToken.AlbumArtist, TagToken.Title, TagToken.BeatsPerMinute };
            
            var setTagDialog = this.setTagDialogFactory.GetSetTagDialog(this.SelectedMp3Entities, tagTokens);
            setTagDialog.SetTagProgressChanged += (sender, args) => this.SetProgressBar(args.ProgressPercentage);
            setTagDialog.SetTagCompleted += (sender, args) => this.ResetProgressBar();
            setTagDialog.ShowDialog();
        }

        #endregion

        #region Winamp

        private void WinampButtonClick(object sender, RoutedEventArgs e)
        {
            if (this.playOnEnqueueCheckbox.IsChecked ?? false)
            {
                this.winamp.Play(this.SelectedMp3Entities);
            }
            else
            {
                this.winamp.Enqueue(this.SelectedMp3Entities);
            }
        }

        private void EnqueueMenuItemClick(object sender, RoutedEventArgs e)
        {
            this.winamp.Enqueue(this.SelectedMp3Entities);
        }

        private void PlayMenuItemClick(object sender, RoutedEventArgs e)
        {
            this.winamp.Play(this.SelectedMp3Entities);
        }

        #endregion

        private void Mp3EntityListListChanged(object sender, ListChangedEventArgs e)
        {
            if (this.mp3EntityList.Count == 0)
            {
                this.batchMenuItem.IsEnabled = false;
                this.winampMenuItem.IsEnabled = false;
            }
            else
            {
                this.batchMenuItem.IsEnabled = true;
                this.winampMenuItem.IsEnabled = true;
            }
        }

        private bool IsValidMp3Path(string mp3Path)
        {
            return !string.IsNullOrEmpty(mp3Path);
        }

        private void AddMp3Entity(IMp3Entity mp3)
        {
            this.mp3EntityList.Add(mp3);
        }

        private void AddMp3Entities(IEnumerable<IMp3Entity> mp3Entities)
        {
            foreach (var mp3 in mp3Entities)
            {
                this.AddMp3Entity(mp3);
            }
        }

        private void SetLoadFilesButtonState(bool enabled)
        {
            this.loadFilesButton.IsEnabled = enabled;
        }

        private void SetProgressBar(int value)
        {
            this.progressBar.Value = value;
        }

        private void ResetProgressBar()
        {
            this.SetProgressBar(0);
        }

        private void WindowLoaded(object sender, RoutedEventArgs e)
        {
            this.SetupMp3DataGrid();
            this.SetupProgressBar();

            this.loadedDirectoryTextbox.Focus();

            this.ApplyDefaultSettings();
        }

        private void SetupMp3DataGrid()
        {
            this.mp3DataGrid.ItemsSource = this.mp3EntityList;
            this.mp3EntityList.ListChanged += this.Mp3EntityListListChanged;
        }

        private void SetupProgressBar()
        {
            this.progressBar.IsEnabled = true;
            this.progressBar.Minimum = 0;
            this.progressBar.Maximum = 100;
        }

        private void ApplyDefaultSettings()
        {
            this.loadedDirectoryTextbox.Text = ConfigurationManager.AppSettings["LoadedDirectory"];
            this.recurseCheckbox.IsChecked = bool.Parse(ConfigurationManager.AppSettings["Recurse"]);
        }
    }
}